package template;

import java.util.ArrayList;
import java.util.List;

import logist.plan.Action;
import logist.plan.Plan;
import logist.plan.Action.Delivery;
import logist.plan.Action.Move;
import logist.plan.Action.Pickup;
import logist.simulation.Vehicle;
import logist.task.Task;
import logist.task.TaskSet;
import logist.topology.Topology;
import logist.topology.Topology.City;

public class Assignment {

	private VehicleTasks[] vehicleTaskActions;
	private List<Vehicle> vehicles;
	private TaskSet auctionedTasks;
	
	public Assignment(List<Vehicle> vehicles, TaskSet tasks) {

		this.vehicles = vehicles;
		this.vehicleTaskActions = new VehicleTasks[vehicles.size()]; 
		for (Vehicle vehicle: vehicles) {
			vehicleTaskActions[vehicle.id()] = new VehicleTasks(vehicle);
		}
		this.auctionedTasks = TaskSet.copyOf(tasks);
		this.selectInitialSolution(tasks);
	}

//	public Assignment(List<Vehicle> vehicles, VehicleTasks[] vehicleTaskActions) {
//		this.vehicles = vehicles;
//		this.vehicleTaskActions = vehicleTaskActions;
//	}

	public Assignment(List<Vehicle> vehicles, VehicleTasks[] vehicleTaskActions, TaskSet auctionedTasks) {
		this.vehicles = vehicles;
		this.vehicleTaskActions = vehicleTaskActions;
		this.auctionedTasks = TaskSet.copyOf(auctionedTasks);
	}

	public Assignment(Assignment oldAssignment) {
		this.vehicles = oldAssignment.vehicles;
		//@todo NOT sure if clone is correct here!!!!!
		this.vehicleTaskActions = new VehicleTasks[this.vehicles.size()];
		for (int i =0; i < this.vehicleTaskActions.length; i++ ) {
			this.vehicleTaskActions[i] = new VehicleTasks(oldAssignment.vehicleTaskActions[i]);
		}
		this.auctionedTasks = TaskSet.copyOf(oldAssignment.auctionedTasks);
	}

	
	public Assignment addNewAuctionedTask(Task task) {
		double minTotalCost = 999999999.9;
		Assignment optimalA = new Assignment(this);
		for (Vehicle v: vehicles) {
			Assignment newA = new Assignment(this);
			newA.auctionedTasks.add(task);
			newA.vehicleTaskActions[v.id()] = vehicleTaskActions[v.id()].findOptimalPosition(task);
			if (newA.getTotalCost() < minTotalCost) {
				minTotalCost = newA.getTotalCost();
				optimalA = new Assignment(newA);
			}
		}
		return optimalA;
	}
	
	private void selectInitialSolution(TaskSet tasks) {
		int maxCapacity = 0, maxVehicle = -1;
		for (Vehicle v: vehicles) {
			if (v.capacity() > maxCapacity) {
				maxCapacity = v.capacity();
				maxVehicle = v.id();
			}
		}
		VehicleTasks[] temp = new VehicleTasks[vehicles.size()];
		int rand = maxVehicle;
		for (Task task: tasks) {
			do {
				//rand = (int)(Math.random() * vehicles.size());
				temp[rand] = new VehicleTasks(this.vehicleTaskActions[rand]);
				temp[rand].addTaskActionPair(task);
			}while (!temp[rand].isValid());
			this.vehicleTaskActions[rand] = new VehicleTasks(temp[rand]);
		}
		System.out.println("initialization finished!");
	}

	public ArrayList<Assignment> chooseNeighboursChangeVehicle(int fromVehicleId, int toVehicleId){


		ArrayList<Assignment> list = new ArrayList<Assignment>();
		ArrayList<VehicleTasks> temp;

		if (vehicleTaskActions[fromVehicleId].getTaskActionList().size() > 0) {
			temp = vehicleTaskActions[fromVehicleId].moveFirstTaskTo(vehicleTaskActions[toVehicleId]);
			if (temp.size() == 0)
				return list;

			VehicleTasks[] newVehicleTaskActions = new VehicleTasks[vehicles.size()]; 

			for (Vehicle vehicle: vehicles) {
				if (vehicle.id() == fromVehicleId ) {
					newVehicleTaskActions[fromVehicleId] = temp.get(0);
				} else if (vehicle.id() == toVehicleId ) {
					newVehicleTaskActions[toVehicleId] = temp.get(1);
				} else {
					newVehicleTaskActions[vehicle.id()] = new VehicleTasks(vehicleTaskActions[vehicle.id()]);
				}
			}

			list.add(new Assignment(vehicles,newVehicleTaskActions,this.auctionedTasks));
		}
		return list;

	}


	public ArrayList<Assignment> chooseNeighbours(){
		ArrayList<Assignment> list = new ArrayList<Assignment>();
		int randomNum = (int)(Math.random()* vehicles.size());

		ArrayList<VehicleTasks> vehicleTasksList = vehicleTaskActions[randomNum].getNeighboursChangeOrder();
		double minCost = vehicleTaskActions[randomNum].getTotalCost();
		VehicleTasks minCostVehicleTasks = vehicleTaskActions[randomNum];

		for (int i = 0; i < vehicleTasksList.size(); i++) {
			double currentCost = vehicleTasksList.get(i).getTotalCost();
			if ( currentCost < minCost ){
				minCost = currentCost;
				minCostVehicleTasks = vehicleTasksList.get(i);
			}
		}

		VehicleTasks[] newVehicleTaskActions = new VehicleTasks[vehicles.size()]; 

		for (Vehicle vehicle: vehicles) {
			if (vehicle.id() == randomNum ) {
				newVehicleTaskActions[vehicle.id()] = minCostVehicleTasks;
			} else {
				newVehicleTaskActions[vehicle.id()] = new VehicleTasks(vehicleTaskActions[vehicle.id()]);
			}
		}

		list.add(new Assignment(vehicles,newVehicleTaskActions,this.auctionedTasks));

		for (int i =0; i< vehicles.size(); i++ ) {
			if (i != randomNum) { 
				ArrayList<Assignment> listTemp = this.chooseNeighboursChangeVehicle(randomNum, i);
				list.addAll(listTemp);
			}
		}

		return list;
	}


	public double getTotalCost(){
		double cost = 0;

		for (int i =0; i< vehicles.size(); i++ ) {
			cost += vehicleTaskActions[i].getTotalCost();
		}

		return cost;
	}


	public Assignment localChoice(ArrayList<Assignment> list){
		Assignment localChoice = null;
		double minCost = 0;


		if (list.size() == 0) {
			System.out.println("??????");
		} else {
			localChoice = list.get(0);
			minCost = localChoice.getTotalCost();
			for (int i = 1; i < list.size(); i++ ){
				double temp = list.get(i).getTotalCost();
				if (temp < minCost) {
					minCost = temp;
					localChoice = list.get(i);
				}
			}

		}

		double p = Math.random();
		if (p < 0.4)
			return localChoice;
		return this;
	}


	private Plan generatePlan(Vehicle vehicle) {
		ArrayList<TaskAction> taskActionList = vehicleTaskActions[vehicle.id()].getTaskActionList();
		if (taskActionList.size() == 0){
			return Plan.EMPTY;
		}
		City currentCity = vehicle.getCurrentCity();
		Plan plan = new Plan(currentCity);

		for (int i = 0; i < taskActionList.size(); i++) {
			TaskAction taskAction= taskActionList.get(i);

			if (currentCity.id != taskAction.getLocation().id) {
				for (City city : currentCity.pathTo(taskAction.getLocation()))
					plan.appendMove(city);
				currentCity = taskAction.getLocation();
			}

			if (taskAction.isPickUp()) {
				plan.appendPickup(taskAction.getTask());
			} else {
				plan.appendDelivery(taskAction.getTask());
			}

		}

		return plan;
	}

	
	public Plan generatePlanNewTaskSet(Vehicle vehicle, TaskSet tasks) {
		ArrayList<TaskAction> taskActionList = vehicleTaskActions[vehicle.id()].getTaskActionList();
		if (taskActionList.size() == 0){
			return Plan.EMPTY;
		}
		
		City currentCity = vehicle.getCurrentCity();
		Plan plan = new Plan(currentCity);

		for (int i = 0; i < taskActionList.size(); i++) {
			TaskAction taskAction= taskActionList.get(i);

			if (currentCity.id != taskAction.getLocation().id) {
				for (City city : currentCity.pathTo(taskAction.getLocation()))
//					listActions.add(new Move(city));
//					newListActions.add(new Move(neighborCity));
					plan.appendMove(city);
				currentCity = taskAction.getLocation();
			}

			if (taskAction.isPickUp()) {
				Task taskTemp = null;
				for (Task t:tasks) {
					if (t.id == taskAction.getTask().id )
						taskTemp = t;
				}
				
//				listActions.add(new Pickup(taskAction.getTask()));
				plan.appendPickup(taskTemp);
				
			} else {
				Task taskTemp = null;
				for (Task t:tasks) {
					if (t.id == taskAction.getTask().id )
						taskTemp = t;
				}

//				listActions.add(new Delivery(taskAction.getTask()));
				plan.appendDelivery(taskTemp);
			}

		}
		return plan;
	}
	
	public List<Plan> getPlans() {
		List<Plan> plans = new ArrayList<Plan>();
		for (Vehicle v: vehicles) {
			plans.add(generatePlan(v));
		}
		return plans;
	}

	public List<Plan> getPlansNewTaskSet(TaskSet tasks) {
		List<Plan> plans = new ArrayList<Plan>();
		for (Vehicle v: vehicles) {
			plans.add(generatePlanNewTaskSet(v, tasks));
		}
		return plans;
	}

}
